////////////////////////////////////////////////////////////////////////////////

#ifndef _CLIENTNETWORK_H_
#include "clientnetwork.h"
#endif

////////////////////////////////////////////////////////////////////////////////

#ifndef _CLIENT_H_
#include "client.h"
#endif

#ifndef _NETWORKPACKET_H_
#include "networkpacket.h"
#endif

#ifndef _CLIENTINFOPACKET_H_
#include "clientinfopacket.h"
#endif

#ifndef _SAVER_H_
#include "saver.h"
#endif

#ifndef _LOADER_H_
#include "loader.h"
#endif

#ifndef _BOOSTSERVICE_H_
#include "boostservice.h"
#endif

#include <cassert>
#include <iostream>

////////////////////////////////////////////////////////////////////////////////


ClientNetwork::ClientNetwork()
: m_Connection( NetworkFactory::GetClientConnection() )
{
    // Setup observer
    AddEventToObserve( Observer::NETWORK_SEND_COMPLETE_TCP );
    AddEventToObserve( Observer::NETWORK_RECEIVE_COMPLETE_TCP );
    AddEventToObserve( Observer::NETWORK_RECEIVE_COMPLETE_UDP );

    m_Connection->AddObserver(this);
}


////////////////////////////////////////////////////////////////////////////////


ClientNetwork::~ClientNetwork()
{
    m_Connection->RemoveObserver(this);
}


////////////////////////////////////////////////////////////////////////////////


void ClientNetwork::Connect(const std::string& _address, const std::string& _nickName)
{
    // Get server address
    // std::cout<<"Server: ";

    //std::string address = "localhost";

    //std::cin>>address;

    try
    {
        // Connect to server
        m_Connection->Connect(_address.c_str(), 27027);
    }
    catch (...)
    {
        std::cout<<"Server not found"<<std::endl;
    }

    ClientInfoPacket packet( ClientInfo(_nickName.c_str()) );
    SendClientInfo(packet);
}


////////////////////////////////////////////////////////////////////////////////


void ClientNetwork::HandleReceiveTCP()
{
    // Get received data
    Buffer buffer( m_Connection->GetTCP().Data(),
                   m_Connection->GetTCP().Size() );

    // Compose packet from it and process it
    ProcessPacketInBuffer(buffer);
}


////////////////////////////////////////////////////////////////////////////////


void ClientNetwork::HandleReceiveUDP()
{
    // Get received data
    Buffer buffer( m_Connection->GetUDP().Data(),
                   m_Connection->GetUDP().Size() );

    // Compose packet from it and process it
    ProcessPacketInBuffer(buffer);
}


////////////////////////////////////////////////////////////////////////////////


void ClientNetwork::Start( Client* _client, const std::string& _address, const std::string& _nickName )
{
    assert(_client);
    m_Client = _client;

    // Connect to server
    Connect(_address, _nickName);

    // Run async operations
    BoostService::Run();
}


////////////////////////////////////////////////////////////////////////////////


void ClientNetwork::OnEvent( Event _event, Observable* _observable )
{
    switch (_event)
    {
    case Observer::NETWORK_RECEIVE_COMPLETE_TCP:
        HandleReceiveTCP();
        break;

    case Observer::NETWORK_RECEIVE_COMPLETE_UDP:
        HandleReceiveUDP();
        break;

    default:
        break;
    }
}


////////////////////////////////////////////////////////////////////////////////


void ClientNetwork::SendClientInfo( const ClientInfoPacket& _clientInfoPacket )
{
    SendTCP(_clientInfoPacket);
}


////////////////////////////////////////////////////////////////////////////////


void ClientNetwork::SendClientInput( const ClientInputPacket& _clientInputPacket )
{
    SendUDP(_clientInputPacket);
}


////////////////////////////////////////////////////////////////////////////////


void ClientNetwork::SendTCP( const Packet& _packet )
{
    // Save packet to buffer
    Saver saver;
    _packet.AcceptSaver(saver);

    // Compose a network packet from it
    NetworkPacket networkPacket( saver.GetBuffer().Data(),
                                 saver.GetBuffer().Size() );

    // Send it via TCP
    m_Connection->SendTCP(networkPacket);
}


////////////////////////////////////////////////////////////////////////////////


void ClientNetwork::SendUDP( const Packet& _packet )
{
    // Save packet to buffer
    Saver saver;
    _packet.AcceptSaver(saver);

    // Compose a network packet from it
    NetworkPacket networkPacket( saver.GetBuffer().Data(),
                                 saver.GetBuffer().Size() );

    // Send it via TCP
    m_Connection->SendUDP(networkPacket);
}


////////////////////////////////////////////////////////////////////////////////


void ClientNetwork::ProcessPacketInBuffer( Buffer& _buffer )
{
    // Read packet code
    Packet::PacketCode code;
    _buffer.ReadMemory( &code, sizeof(Packet::PacketCode) );

    // Setup loader
    Loader loader(_buffer);

    // Load packet
    switch (code)
    {
    case Packet::SERVER_STATE:
        {
            ServerStatePacket serverStatePacket = loader.LoadServerStatePacket();
            m_Client->ReceiveServerState(serverStatePacket);
            break;
        }

    case Packet::SERVER_INFO:
        {
            ServerInfoPacket serverInfoPacket = loader.LoadServerInfoPacket();
            m_Client->ReceiveServerInfo(serverInfoPacket);
            break;
        }

    case Packet::SERVER_EVENT:
        {
            ServerEventPacket serverEventPacket = loader.LoadServerEventPacket();
            m_Client->ReceiveServerEvent(serverEventPacket);
            break;
        }

    case Packet::SERVER_START:
        {
            ServerStartPacket serverStartPacket = loader.LoadServerStartPacket();
            m_Client->ReceiveServerStart(serverStartPacket);
            std::cout << "Map name recieved.\n";
            break;
        }

    case Packet::CLIENT_ID:
        {
            ClientIDPacket clientIDPacket = loader.LoadClientIDPacket();
            m_Connection->SetupUDP( clientIDPacket.GetClientID() );
            m_Client->SetID( clientIDPacket.GetClientID() );
            break;
        }

    case Packet::SERVER_STATISTICS:
        {
            ServerStatisticsPacket serverStatisticsPacket = loader.LoadServerStatisticsPacket();
                m_Client->ReceiveServerStatistics( serverStatisticsPacket.GetStatistics() );
            break;
        }

    default:
        throw "not implemented";
    }
}


////////////////////////////////////////////////////////////////////////////////
